﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using uPLibrary.Networking.M2Mqtt;
using uPLibrary.Networking.M2Mqtt.Messages;
using static MQTTDemo.M2MClient;

namespace MQTTDemo
{
    internal class M2MClient
    {

        public delegate void ReceMsgDelegate(string mac,string message);

        ReceMsgDelegate receMsgDelegate = null;

        readonly string mqttUserName = "mqtt";
        readonly string mqttPassword = "2024042801";

        /// <summary>
        /// 这里使用了我们的MQTT测试服务器，你可以更改为你的服务器IP
        /// </summary>
        readonly string hostNameOrIp = "mqtt.nireniot.com";
        /// <summary>
        /// mqtt client id 在mqtt服务端上必须唯一
        /// </summary>
        string clientId = string.Empty;
        /// <summary>
        /// mqtt client 实例，在构造函数中初始化即可
        /// </summary>
        MqttClient client = null;
        /// <summary>
        /// 设备的在线信息管理单元
        /// </summary>
        ConcurrentDictionary<string, DeviceOnlineInfo> dicDevices = new ConcurrentDictionary<string, DeviceOnlineInfo>();


        public M2MClient() { }

        public M2MClient(string hostNameOrIp) {
            if (string.IsNullOrEmpty(hostNameOrIp))
            {
                client = new MqttClient(this.hostNameOrIp);
            }
            else { 
                //使用传进来的域名或Ip
                client = new MqttClient(hostNameOrIp);
            }
            //关联接收事件
            client.MqttMsgPublishReceived += ClientMqttMsgPublishReceived;
            //注意这里的 "/demo/+/user/update" 格式
            client.Subscribe(new string[] { "/mqtt/+/user/update" }, new byte[] { MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE }); // sub 的qos=1

            string clientId = Guid.NewGuid().ToString();
            client.Connect(clientId, this.mqttUserName, this.mqttPassword);
        }

        void ClientMqttMsgPublishReceived(object sender, MqttMsgPublishEventArgs e)
        {
            string receMsg = Encoding.UTF8.GetString(e.Message);
            //设备返回的数据在 e.Message 中
            Console.WriteLine("Received = " + receMsg + " on topic " + e.Topic);
            string mac = this.GetDeviceMac(e.Topic);
            if (string.IsNullOrEmpty(mac))
                return;
            if (!this.dicDevices.ContainsKey(mac)) {
                //不存在即新建一个
                this.dicDevices.TryAdd(mac, new DeviceOnlineInfo(mac));
            } else {
                //重置在线时间
                this.dicDevices[mac].SetLastOnlineTime();
            }
            //通过 delegate 类型将数据取走
            this.receMsgDelegate?.Invoke(mac,receMsg);
        }

        string GetDeviceMac(string topic) {
            if (string.IsNullOrEmpty(topic)) {
                return null;
            }
            //topic 必须为  /mqtt/$mac/user/update
            string[] strs = topic.Split('/');
            if (strs.Length < 4) { 
                return null;
            }
            return strs[2];
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="mac">设备的mac地址</param>
        /// <param name="atCmd">要发送的指令</param>
        /// <returns></returns>
        public Boolean SendMsg(string mac,string atCmd) {
            if(string.IsNullOrEmpty(mac) || string.IsNullOrEmpty(atCmd)) {
                return false;
            }
            if (!(atCmd.Contains("\r\n")) && !atCmd.Contains("\n"))
            {
                atCmd += "\r\n";
            }
            if (this.client == null || !this.client.IsConnected) {
                return false;
            }
            string topic = "/mqtt/" + mac + "/user/get";
            Console.WriteLine("向主题<" + topic + ">发送指令<" + atCmd + ">");
            //通过 mac 区分不同的设备
            client.Publish(topic, Encoding.UTF8.GetBytes(atCmd), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);
            return true;
        }

        public void SetReceMsgDelegate(ReceMsgDelegate receMsgDelegate)
        {
            this.receMsgDelegate += receMsgDelegate;
        }

        /// <summary>
        /// 获取设备的在线与离线状态列表
        /// </summary>
        /// <param name="isOnline">true获取在线列表，false获取离线列表</param>
        /// <returns></returns>
        public List<string> GetOnlineDeviceList(bool isOnline)
        {
            List<string> macs = new List<string>();
            List<DeviceOnlineInfo> infos = new List<DeviceOnlineInfo>();
            List<string> keys = this.dicDevices.Keys.ToList();
            foreach (string key in keys)
            {
                DeviceOnlineInfo info = this.dicDevices[key];
                if (info == null)
                    continue;
                if (isOnline)
                {
                    //获取在线的设备，所以超时的不需要
                    if (info.IsTimeout())
                        continue;
                }
                else { 
                    //获取离线的设备，所以不超时的不需要
                    if(!info.IsTimeout())
                        continue;
                }
                infos.Add(info);
            }
            if(infos.Count > 0) {
                //有数据，即将设备的mac添加到macs列表中
                foreach (DeviceOnlineInfo info in infos)
                {
                    macs.Add(info.GetMac());
                }
            }
            return macs;
        }

        

    }
}
